Skip to content

Securing JSON RPC

Overview

JSON RPC servers are secured via a security plugin interface. The official implementation is Quorum Security Plugin which enables Quorum Client to protect JSON RPC APIs with the following features:

Native Transport Layer Security

The native Transport Layer Security (TLS) introduces an encryption layer to the JSON-RPC request/response communication channel for both HTTP, and Web Socket listeners. By using a simple configuration flag this feature allows the automatic generation of self signed certificate for testing environment, or a smooth integration with certificate authorities for enterprise deployment.

Enterprise Authorization Protocol Integration

Enterprise authorization protocol integration introduces an access control layer that authorizes each JSON RPC invocation to an atomic module function level (E.g personal_OpenWallet) using industry standard OAuth 2.0 protocol and/or JSON Web Token (JWT) method. This feature allows managing distributed application (dApps), and Quorum Clients access control in an efficient approach.

Configuration

Please refer to plugin implementation for more details.

There are also examples on how to configure the plugin to work with different OAuth2 Authorization servers.

Client Usage

Before invoking protected JSON RPC APIs, the client must request an access token by authenticating with the authorization server. An access token could be opaque or a JWT. It’s the client’s reponsiblity to maintain this preauthenticated token valid during its life time.

When invoking a JSON RPC API, the client must send the preauthenticated token in the Authorization request header field with Bearer authentication scheme. All major HTTP client libraries have extensions to allow such customization.

Examples

Here are some examples on how to interact with protected JSON RPC APIs:

web3

let Web3 = require('web3');
let HttpHeaderProvider = require('httpheaderprovider');
// obtain the preauthenticated bearer token 
// by authenticating with the authorization server
let token = ...;
let headers = { "Authorization": `Bearer ${token}` };
let provider = new HttpHeaderProvider('https://...', headers);
web3.setProvider(provider);

curl

# obtain the preauthenticated bearer token 
# by authenticating with the authorization server
export TOKEN="Bearer ..."
curl -X POST -H "Content-type: application/json" -H "Authorization: $TOKEN" \
    https://... \
    --data '{"jsonrpc":"2.0", "method":"eth_blockNumber", "params":[], "id":1}'

geth attach

There are additional flags allowing to connect to secured Quorum node

--rpcclitoken value                 RPC Client access token
--rpcclitls.insecureskipverify      Disable verification of server's TLS certificate on connection by client
--rpcclitls.cert value              Server's TLS certificate PEM file on connection by client
--rpcclitls.cacert value            CA certificate PEM file for provided server's TLS certificate on connection by client
--rpcclitls.ciphersuites value      Customize supported cipher suites when using TLS connection. Value is a comma-separated cipher suite string

E.g.: Connect to the node with --rpcclitls.insecureskipverify to ignore the Server’s certificate validation.

geth attach https://localhost:22000 --rpcclitls.insecureskipverify    
geth attach wss://localhost:23000   --rpcclitls.insecureskipverify    

ethclient

ethclient provides a client for Ethereum RPC API. It’s also enhanced to support Quorum-specific APIs and ability to invoke protected APIs.

HTTP/HTTPS

For HTTP endpoint, the preauthenticated token is populated in Authorization HTTP request header for each call. The token value is obtained from rpc.HttpCredentialsProviderFunc implementation which is configured after rpc.Client is instantiated.

// obtain the preauthenticated bearer token 
// by authenticating with the authorization server
token := ...
// instantiate rpc.Client
c, err := rpc.Dial("http://...")
if err != nil {
    // handle err
}
var f rpc.HttpCredentialsProviderFunc = func(ctx context.Context) (string, error) {
    // optionally to refresh the token if necessary
    return "Bearer " + token, nil
}
// configure rpc.Client with preauthenticated token
authenticatedClient, err := c.WithHTTPCredentials(f)
if err != nil {
    // handle err
}

// use authenticatedClient as usual

To customize TLS client configuration:

// instantiate a http.Client with custom TLS client config
myHttpClient := ... 
// instantiate rpc.Client
c, err := rpc.DialHTTPWithClient("https://...", myHttpClient)

WS/WSS

For WS endpoint, the preauthenticated token is populated in Authorization HTTP request header only once during the handshake. The token value is obtained from rpc.HttpCredentialsProviderFunc implementation via context.Context when dialing.

// obtain the preauthenticated bearer token 
// by authenticating with the authorization server
token := ...

var f rpc.HttpCredentialsProviderFunc = func(ctx context.Context) (string, error) {
    // optionally to refresh the token if necessary
    return "Bearer " + token, nil
}
ctx := context.WithValue(context.Background(), rpc.CtxCredentialsProvider, f)
authenticatedClient, err := rpc.DialContext(ctx, "ws://...)
if err != nil {
    // handle err
}

// use authenticatedClient as usual

To customize TLS client configuration, use rpc.DialWebsocketWithCustomTLS() instead of rpc.DialContext()

// create a tls.Config
tlsConfig := &tls.Config{...}
c, err := rpc.DialWebsocketWithCustomTLS(ctx, "wss://...", "", tlsConfig)